package com.khoayang.littlefighterchaos.game;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import org.cocos2d.actions.base.CCAction;
import org.cocos2d.actions.base.CCRepeatForever;
import org.cocos2d.actions.interval.CCJumpBy;
import org.cocos2d.nodes.CCAnimation;
import org.cocos2d.nodes.CCSprite;
import org.cocos2d.sound.SoundEngine;
import org.cocos2d.types.CGPoint;
import org.cocos2d.types.CGRect;
import org.cocos2d.types.CGSize;

import android.content.Context;
import android.util.Log;

import com.khoayang.littlefighterchaos.game.GamePad.KEY;

public class CharacterGame {
	
	private State _State;
	private CGPoint _Position;
	private CGSize _Size;
	private float _HP;
	private float _CurrentHP;
	private float _MP;
	private float _CurrentMP;
	private Map<String, CCAction> _AllAnimation;
	private CCAction _DefaulAction;
	private Direction _Direction;
	private boolean _IsSpecialAction = false;
	private int _ID;
	private ArrayList<AttackingObjectListener> _AttackingObjectObserver;
	private Bar _HPBar;
	private Bar _MPBar;
	private Context _Context;
	private CCSprite _Sprite;
	private CGPoint _StartJumpPoint;
	float _JumpSpeed = 0;
	private ArrayList<DyingListener> _DyingListeners;
	
	public enum Direction {
		LEFT, RIGHT, UP, DOWN
	}

	public CharacterGame() {
		_DyingListeners = new ArrayList<DyingListener>();
		_AttackingObjectObserver = new ArrayList<AttackingObjectListener>();
		_AllAnimation = new HashMap<String, CCAction>();
		_Direction = Direction.RIGHT;
	}
	
	public CCSprite get_Sprite() {
		return _Sprite;
	}

	public void set_Sprite(CCSprite _Sprite) {
		this._Sprite = _Sprite;
	}

	public void runDefaultAction() {
		_DefaulAction = getAnimateByName("Idle");
		_Sprite.runAction(_DefaulAction);
	}

	private CCAction getAnimateByName(String animationName) {
		CCAction action = _State.getAnimationSkill(animationName);
		return action;
	}

	public State get_State() {
		return _State;
	}

	public void set_State(State _State) {
		this._State = _State;
	}

	public CGPoint get_Position() {
		return _Position;
	}

	public void set_Position(CGPoint position) {
		if (position == null)
			position = CGPoint.ccp(0f, 0f);
		this._Position = position;
		_Sprite.setPosition(position);
		if (_HPBar != null)
			_HPBar.set_Position(CGPoint.ccp(position.x, position.y + 70));
		if (_MPBar != null)
			_MPBar.set_Position(CGPoint.ccp(position.x, position.y + 50));
	}

	public CGSize get_Size() {
		return _Size;
	}

	public void set_Size(CGSize _Size) {
		this._Size = _Size;
	}

	public float get_HP() {
		return _HP;
	}

	public void set_HP(float _HP) {
		this._HP = _HP;
		this._CurrentHP = _HP;
	}

	public float get_MP() {
		return _MP;
	}

	public void set_MP(float _MP) {
		this._MP = _MP;
		this._CurrentMP = _MP;
	}

	public void startAction(String actionName) {
		if (_State.get_CurrentSkill() instanceof DyingSkill) {
			return;
		}
		
		if(_IsSpecialAction && actionName.compareTo("Die") != 0)
			return;
		
		
		//Get animation
		CCAction action;
		if (_AllAnimation.containsKey(actionName))
			action = _AllAnimation.get(actionName);
		else {
			action = getAnimateByName(actionName);
			if (action == null)
				return;
			_AllAnimation.put(actionName, action);
		}
		
		//check is action ready to use
		
		if (skillReady(actionName)) {
			CCAction currentAction = _State.get_CurrentAction();
			if (currentAction != null) {
				_State.set_PreviousAction(currentAction);
				_Sprite.stopAction(currentAction);
			}
			set_CurrentMP(_CurrentMP - _State.getMPConsume(actionName));

			if (action instanceof CCRepeatForever)
				set_IsSpecialAction(false);
			else
				set_IsSpecialAction(true);
			_State.set_CurrentAction(action);
			
			//play sound effect
			if(GameManagerContent.Instance().get_UseEffectSound())
			{
			String soundEffectStr = _State.get_CurrentSkill().get_SoundEffect();
			if (soundEffectStr != null) {
				if (soundEffectStr.length() != 0) {
					int resID = Util.getIDFromRawRes(_State.get_CurrentSkill()
							.get_SoundEffect(), _Context);
					SoundEngine.sharedEngine().playEffect(_Context, resID);
				}
			}
			}
			
			//Some special action
			if (_State.get_CurrentSkill() instanceof DyingSkill) {
				_Sprite.stopAllActions();
				 CCAnimation dyingAnimation =_State.get_CurrentSkill().get_Animation();
				 _Sprite.setDisplayFrame(dyingAnimation.frames().get(dyingAnimation.frames().size()-1));
			}
			
			if (_State.get_CurrentSkill() instanceof JumpingSkill) {
				_StartJumpPoint = CGPoint.ccp(_Position.x, _Position.y);
				_JumpSpeed = ((JumpingSkill)_State.get_CurrentSkill()).get_Speed();
			}
			
			_Sprite.runAction(action);
		}
		// Log
		if (_State.get_CurrentSkill() != null)
			Log.w("Character current skill", _State.get_Name() + " "
					+ _State.get_CurrentSkill().get_SkillName());
		else
			Log.w("Character current skill", "null");
		if (_State.get_PreSkill() != null)
			Log.w("Character prev skill",  _State.get_Name() + " "
					+ _State.get_PreSkill().get_SkillName());
		else
			Log.w("Character prev skill", "null");
	}

	public boolean skillReady(String actionName) {
		float remainMP = _CurrentMP - _State.getMPConsume(actionName);
		return remainMP > 0;
	}

	public float getScaleX() {
		return _Sprite.getScaleX();
	}

	public void setScaleX(float f) {
		_Sprite.setScaleX(f);
	}

	public void stopAction() {
		CCAction currentAction = _State.get_CurrentAction();
		if (currentAction != null) {
			_Sprite.stopAction(currentAction);
			_State.set_CurrentAction(null);
		}
		CCAction previousAction = _State.get_PreviousAction();
		if (previousAction != null) {
			_State.set_CurrentAction(previousAction);
			_Sprite.runAction(currentAction);
			_State.set_PreviousAction(null);
		}
	}

	public CharacterGame clone() {
		CharacterGame character = new CharacterGame();
		character.set_HP(_HP);
		character.set_MP(_MP);
		character.set_Sprite(CCSprite.sprite(_Sprite.getTexture()));
		character.set_State(_State.clone());
		character.set_Position(_Position);
		character.set_Size(_Size);
		character.set_HPBar(_HPBar.clone());
		character.set_MPBar(_MPBar.clone());
		return character;
	}

	public void changeState() {
		_AllAnimation = new HashMap<String, CCAction>();
	}

	public void startJump() {
		CCJumpBy jump = CCJumpBy.action(2f, _Sprite.getPosition(), 100, 2);
		_Sprite.runAction(jump);
	}

	public void update(ArrayList<KEY> _KeyState) {
		
		if (_State.get_CurrentSkill() instanceof DyingSkill) {
			if(_State.get_CurrentAction().isDone()){
				notifyDyingEvent();
			}
			return;
		}
		
		if (is_IsSpecialAction()) {
			if (_State.get_CurrentAction().isDone()) {
				if (_State.get_CurrentSkill() instanceof AttackingSkill)
					initAttackingObjects();
				else if (_State.get_CurrentSkill() instanceof TransformingSkill) {
					String newFormName = ((TransformingSkill)_State.get_CurrentSkill()).get_NewFormName();
					_Sprite.stopAllActions();
					CharacterFactory characterfactory = CharacterFactory.getInstance(_Context);
					_State = characterfactory.cloneStateByNameCharacter(newFormName);
					_AllAnimation.clear();
				}

				set_IsSpecialAction(false);
				resetAllActionState();
			}else{
				if (_State.get_CurrentSkill() instanceof JumpingSkill) {
					updateJump();
				}
			}
		}
		
		CGPoint pos = _Position;
		float currentVelocity = 0;
		if (_State.get_CurrentAction() != null) {
			currentVelocity = _State.get_CurrentSkill().get_Velocity();
		}
		// update MP
		if (_CurrentMP + _State.get_AutoMPRegen() <= _MP) {
			set_CurrentMP(_CurrentMP + _State.get_AutoMPRegen());
		}

		// Process pressed keys
		for (int i = 0; i < _KeyState.size(); i++) {
			KEY currentKeyDown = _KeyState.get(i);
			switch (currentKeyDown) {
			case LEFT:
				if (_Direction == Direction.RIGHT) {
					setScaleX(-getScaleX());
					_Direction = Direction.LEFT;
				}
				pos.x -= currentVelocity;
				break;
			case RIGHT:
				if (_Direction == Direction.LEFT) {
					setScaleX(-getScaleX());
					_Direction = Direction.RIGHT;
				}
				pos.x += currentVelocity;
				break;
			case UP:
				pos.y += currentVelocity;
				break;
			case DOWN:
				pos.y -= currentVelocity;
				break;
			default:
				break;
			}
			set_Position(pos);
		}
	}

	public void setDyingListener(DyingListener dyingListener){
		_DyingListeners.add(dyingListener);
	}
	
	private void notifyDyingEvent() {
		for(int i=0; i<_DyingListeners.size(); i++){
			_DyingListeners.get(i).dyingPerform(this);
		}
	}

	public void resetAllActionState() {
		_Sprite.stopAllActions();
		runDefaultAction();
		_State.set_CurrentAction(null);
		_State.set_PreviousAction(null);
	}

	
	private void updateJump() {
		JumpingSkill jumpSkill = (JumpingSkill)_State.get_CurrentSkill();
		CGPoint pos = _Position;
		if(_Position.y - _StartJumpPoint.y > jumpSkill.get_Height()){
			_JumpSpeed = - _JumpSpeed;
		}
		pos.y += _JumpSpeed;
		set_Position(pos);
		if(pos.y < _StartJumpPoint.y){
			set_IsSpecialAction(false);
			resetAllActionState();
		}
	}

	private void initAttackingObjects() {
		AttackingSkill attackingSkill = (AttackingSkill) _State
				.get_CurrentSkill();
		ArrayList<AttackingObject> attackingObjects = attackingSkill
				.get_AttackingObjects();
		for (int i = 0; i < attackingObjects.size(); i++) {
			AttackingObject attackingObject = attackingObjects.get(i);
			CGPoint pos = CGPoint.ccp(_Position.x, _Position.y);
			Direction dir = _Direction;
			CGPoint startPoint = attackingObject.get_StartPoint();
			if (_Direction == Direction.LEFT)
				if(attackingObject.get_Direction() == Direction.LEFT){
					pos.x -= startPoint.x;
					pos.y -= startPoint.y;
				}else{
					pos.x += startPoint.x;
					pos.y += startPoint.y;
					dir = Direction.RIGHT;
				}
			else if (_Direction == Direction.RIGHT)
				if(attackingObject.get_Direction() == Direction.LEFT){
					pos.x += startPoint.x;
					pos.y += startPoint.y;
				}else{
					pos.x -= startPoint.x;
					pos.y -= startPoint.y;
					dir = Direction.LEFT;
				}
			attackingObject.set_Position(pos);
			attackingObject.set_Owner(_ID);
			attackingObject.set_Direction(dir);
			notifyToObserverAttackingObject(attackingObject);
			attackingObject.runAnimation();
		}
	}

	private void notifyToObserverAttackingObject(AttackingObject attackingObject) {
		for (int i = 0; i < _AttackingObjectObserver.size(); i++) {
			_AttackingObjectObserver.get(i).addNewAttackingObject(
					attackingObject);
		}
	}

	public String getActionName(ArrayList<KEY> _KeyState) {
		for (int i = 0; i < _State.get_Commands().size(); i++) {
			Command command = _State.get_Commands().get(i);
			if (_KeyState.size() != command.get_Keys().size())
				continue;
			else {
				boolean equal = true;
				for (int j = 0; j < _KeyState.size(); j++) {
					KEY k = command.get_Keys().get(j);
					if (k == KEY.RIGHT
							&& (_KeyState.get(j) == KEY.LEFT || _KeyState
									.get(j) == KEY.RIGHT)) {
						continue;
					}
					if (_KeyState.get(j) != k) {
						equal = false;
						break;
					}
				}
				if (equal) {
					return command.get_CommandNames();
				}
			}
		}
		return null;
	}

	public boolean is_IsSpecialAction() {
		return _IsSpecialAction;
	}

	public void set_IsSpecialAction(boolean _IsSpecialAction) {
		this._IsSpecialAction = _IsSpecialAction;
	}

	public int get_ID() {
		return _ID;
	}

	public void set_ID(int _ID) {
		this._ID = _ID;
	}

	public void addObserverAttackingObject(AttackingObjectListener gameLayer) {
		_AttackingObjectObserver.add(gameLayer);
	}

	public Bar get_HPBar() {
		return _HPBar;
	}

	public void set_HPBar(Bar _HPBar) {
		this._HPBar = _HPBar;
	}

	public Bar get_MPBar() {
		return _MPBar;
	}

	public void set_MPBar(Bar _MPBar) {
		this._MPBar = _MPBar;
	}

	public float get_CurrentHP() {
		return _CurrentHP;
	}

	public void set_CurrentHP(float _CurrentHP) {
		this._CurrentHP = _CurrentHP;
		_HPBar.set_FillPercent(_CurrentHP * 100.0f / _HP);
	}

	public float get_CurrentMP() {
		return _CurrentMP;
	}

	public void set_CurrentMP(float _CurrentMP) {
		this._CurrentMP = _CurrentMP;
		_MPBar.set_FillPercent(_CurrentMP * 100.0f / _MP);
	}

	public void beHit(AttackingObject attackingObject) {
		float remainHp = get_CurrentHP() - attackingObject.get_AttackValue();
		if (remainHp > 0) {
			set_CurrentHP(remainHp);
			startAction(attackingObject.get_BeHitActionName());
		} else {
			set_CurrentHP(0);
			startAction("Die");
		}
	}

	public void beHit(CharacterGame _Character) {
		AttackingSkill enemyAttackingSkill = (AttackingSkill) _Character
				.get_State().get_CurrentSkill();
		if (enemyAttackingSkill.get_AttackPoint() != 0) {
			float defendPoint = 0;
			if(_State.get_CurrentSkill() instanceof DefendingSkill)
			{
				defendPoint = ((DefendingSkill)_State.get_CurrentSkill()).get_DefendPoint();
				if(defendPoint >= enemyAttackingSkill.get_AttackPoint()){
					return;
				}
			}
			float remainHP = _CurrentHP - (enemyAttackingSkill.get_AttackPoint() - defendPoint);
			if (remainHP > 0) {
				set_CurrentHP(remainHP);
				startAction(enemyAttackingSkill.get_BeHitActionName());
			} else {
				set_CurrentHP(0);
				startAction("Die");
			}
		}
	}

	public void setContext(Context context) {
		_Context = context;
	}
	
	public void setOrder()
	{
		if(_Sprite.getParent() != null)
			_Sprite.getParent().reorderChild(_Sprite, 2000 - (int)(_Position.y - _Sprite.getContentSize().height/2));
		if(_HPBar.get_SpriteBar().getParent() != null){
			_HPBar.get_SpriteBar().getParent().reorderChild(_HPBar.get_SpriteBar(), 2000 - (int)(_Position.y - _Sprite.getContentSize().height/2));
			_HPBar.get_SpriteFillBar().getParent().reorderChild(_HPBar.get_SpriteFillBar(), 2000 - (int)(_Position.y - _Sprite.getContentSize().height/2));
		}
		if(_MPBar.get_SpriteFillBar().getParent() != null){
			_MPBar.get_SpriteBar().getParent().reorderChild(_MPBar.get_SpriteBar(), 2000 - (int)(_Position.y - _Sprite.getContentSize().height/2));
			_MPBar.get_SpriteFillBar().getParent().reorderChild(_MPBar.get_SpriteFillBar(), 2000 - (int)(_Position.y - _Sprite.getContentSize().height/2));
		}
	}
	
	public CGRect getRect()
	{
		CGRect rect = CGRect.make(_Sprite.getPosition().x - _Size.width/2, _Sprite.getPosition().y - _Size.height/2, 
						_Size.width, _Size.width);
		return rect;
	}
}
